#include "m_shader.h"
#include <string>
#include <iostream>
#include <sstream>
#include <fstream>
#include "glad/glad.h"
using namespace std;

Shader::Shader(const char *vp, const char *fp)
{
    readShaderFile(vp, fp);
}

void Shader::setShader(const char *vp, const char *fp)
{
    readShaderFile(vp, fp);
}

void Shader::readShaderFile(const char *vpath, const char *fpath)
{
    string vCode, fCode;
    ifstream vFile, fFile;
    vFile.exceptions(ifstream::failbit | ifstream::badbit);
    fFile.exceptions(ifstream::failbit | ifstream::badbit);
    bool fail = false;
    try
    {
        vFile.open(vpath);
        stringstream vStr;
        vStr << vFile.rdbuf();
        vFile.close();
        vCode = vStr.str();
    }
    catch (const ifstream::failure &e)
    {
        cerr << "Failed to read the vertex shader: " << e.what() << '\n';
        fail = true;
    }
    try
    {
        fFile.open(fpath);
        stringstream fStr;
        fStr << fFile.rdbuf();
        fFile.close();
        fCode = fStr.str();
    }
    catch (const ifstream::failure &e)
    {
        cerr << "Failed to read the fragment shader: " << e.what() << '\n';
        fail = true;
    }
    if (fail)
        exit(-1);
    compileShader(vCode.c_str(), fCode.c_str());
}

void Shader::compileShader(const char *vs, const char *fs)
{
    unsigned int vshader, fshader;
    int success;
    char infolog[512];

    vshader = glCreateShader(GL_VERTEX_SHADER);
    glShaderSource(vshader, 1, &vs, NULL);
    glCompileShader(vshader);
    glGetShaderiv(vshader, GL_COMPILE_STATUS, &success);
    if (!success)
    {
        glGetShaderInfoLog(vshader, 512, NULL, infolog);
        cout << "Vertex shader compilation error:\n"
             << infolog << endl;
        exit(-1);
    }

    fshader = glCreateShader(GL_FRAGMENT_SHADER);
    glShaderSource(fshader, 1, &fs, NULL);
    glCompileShader(fshader);
    glGetShaderiv(fshader, GL_COMPILE_STATUS, &success);
    if (!success)
    {
        glGetShaderInfoLog(fshader, 512, NULL, infolog);
        cout << "Fragment shader compilation error:\n"
             << infolog << endl;
        exit(-1);
    }

    ID = glCreateProgram();
    glAttachShader(ID, vshader);
    glAttachShader(ID, fshader);
    glLinkProgram(ID);
    glGetShaderiv(ID, GL_LINK_STATUS, &success);
    if (!success)
    {
        glGetShaderInfoLog(ID, 512, NULL, infolog);
        cout << "Shader program linking error:\n"
             << infolog << endl;
        exit(-1);
    }
    glDeleteShader(vshader);
    glDeleteShader(fshader);
}
shader_t Shader::id() const
{
    return ID;
}

void Shader::use() const
{
    glUseProgram(ID);
}

int Shader::uniform(const char *name) const
{
    return glGetUniformLocation(ID, name);
}